This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

##install.packages("dplyr")
##install.packages("jsonlite")
##install.packages("rjson")
##install.packages("ggplot2")
library(conflicted)
library(rjson)
library(dplyr)
library(jsonlite)
library(tidyverse)
library(ggplot2)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

proyecto stream exitoso en spotify

El objetivo de este proyecto es analizar mediante los datos por niveles de un JSON y los datos de entrada de un CSV que contienen los id de los streaming y las pistas escuchadas, cual es el estream y la canción mas escuchada y observar la relación entre un rango de duración de las mismas para determinar entre las mejores canciones si hay alguna influencia en la duración de la canción para su éxito para ser tendencia.

Cargamos el primer dataset

primero leemos la data desde el propio JSON

df <- fromJSON("C:/Users/Edgar/Documents/GitHub/DataMining_and_MachineLearning_EdgarV/data/Proyecto1/challenge_set.json")
df
$date
[1] "2018-01-16 08:47:28.198015"

$version
[1] "v1"

$playlists

$name
[1] "build/challenge/challenge_set.json"

$description
[1] "the challenge set for the RecSys Challenge 2018"

Pasamos a un data frame los dtos mas relevantes para nosotros que sería la Playlist

df_spotify <- df$playlists
df_spotify <- df_spotify%>%
  mutate(indice = row.names(.))
df_spotify

Exploramos los datos

df_tracks <- df_spotify$tracks
head(df_tracks)
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]
## observamos que para cada lista de pistas hay valores asociados a cada pista como nombre de artista, nombre de la pista y duracion

verificamos si ningun dato tiene elementos NA


any(is.na(df_tracks))
[1] FALSE

Cargamos el segundo dataset

eliminaremos columnas que no nos sirvan, pero antes debemos analizar el dataset de los numeros de streamings

df_data_spotify <- read.csv("C:/Users/Edgar/Documents/GitHub/DataMining_and_MachineLearning_EdgarV/data/Proyecto1/sample_submission.csv")

## definimos cual será la primera columna

primera_columna <- df_data_spotify$X1048588
##head(primera_columna,10)

## transformamos las filas restantes en listas
listas_filas <- lapply(1:nrow(df_data_spotify),function(i) as.list(t(df_data_spotify[i,-1])))

##listas_filas

df_lista_filas <- data.frame(tracks_uri = unlist(listas_filas))
df_lista_filas

## combinamos las columnas de los pid con las listas
##df_data_spotify_combinado <- data.frame(pid = primera_columna,tracks_uri = unlistlistas_filas)
##head(df_data_spotify_combinado,2)
##head(df_data_spotify_combinado$tracks_uri,1)

##head(df_data_spotify_combinado$tracks_uri,2)

exploramos datos por última vez antes de ir a lo nuestro

datos pid

primera_columna <- df_data_spotify$X1048588
df_pid <- data.frame(pid = primera_columna)
head(df_pid,10)

datos tracks_uri

df_data_spotify <- read.csv("C:/Users/Edgar/Documents/GitHub/DataMining_and_MachineLearning_EdgarV/data/Proyecto1/sample_submission.csv")

## transformamos las filas restantes en listas
listas_filas <- lapply(1:nrow(df_data_spotify),function(i) as.list(t(df_data_spotify[i,-1])))

##dataframe de listas_filas
df_tracks_uri <- data.frame(track_uri = unlist(listas_filas))
head(df_tracks_uri,2)

exploramos datos por última vez antes de ir a lo nuestro

datos track_uri

head(df_tracks_uri,1000)

una vez obtenidos los valores que necesitamos procedemos a contarlos

primero empezaremos con los datos pid que nos da la información de cuantos streamings hubo en el proceso de recolección de datos

df_numero_streamings <- df_pid%>%
  select(pid)%>%
  count()
df_numero_streamings
print("ojo que hay que descontar 1000 datos que estaban vacios al realizar la limpieza de los datos ")
[1] "ojo que hay que descontar 1000 datos que estaban vacios al realizar la limpieza de los datos "

###revisamos si hay datos duplicados

df_numero_streamings_duplicados <- df_pid%>%
  filter(duplicated(pid))%>%
  count()
df_numero_streamings_duplicados

con esto comprobamos que hay 10000 datos de sesión de streamings

ahora verifiquemos si hay datos repetidos en la columna de tracks_uri que nos muestra la información de la pista

df_numero_tracks_repetidos <- df_tracks_uri%>%
  filter(duplicated(tracks_uri))%>%
  count()
df_numero_tracks_repetidos

ahora contamos los datos totales de las pistal

df_numero_total_tracks <- df_tracks_uri%>%
  select(tracks_uri)%>%
  count()
df_numero_total_tracks

realizamos la diferencia para contabilizar el numero real de pistas

num_real_pistas <- df_numero_total_tracks - df_numero_tracks_repetidos
num_real_pistas

ahora contamos los streamings que tiene el otro dataset


num_streamings <- df_spotify%>%
  select(pid)%>%
  count()
num_streamings
NA

por lo visto el número de streamings coinciden con los datos del dataset de las pistas

ahora contamos los datos de las pistas del dataset challenge

primero unimos los dataframes en uno solo

new_df_tracks <- bind_rows(df_tracks, .id = "dataframe_id")
new_df_tracks

ahora contamos el numero de pistas que tiene este dataset

num_tracks <- new_df_tracks%>%
  select(track_uri)%>%
  count()
num_tracks

revisamos si no hay repetidos

num_tracks <- new_df_tracks%>%
  distinct(track_uri)%>%
  count()
num_tracks

comparamos con el número real de pistas

comparison <- data.frame(num_tracks_JSON=num_real_pistas, num_tracks_csv= num_tracks )
comparison

por lo tanto los datos van acorde con este proyecto

Objetivo 1.-

##Extraer las características de los streaming de canciones y de sus pistas como su nombre, artista, el número de pistas (en el caso del Streaming) y la duración de la pista.

head(df_spotify,10)
head(new_df_tracks,10)

Objetivo 2.-

Hallar las pistas mas sonadas y los streamings más escuchados.

pistas_mas_sonadas <- left_join(new_df_tracks, df_tracks_uri, by="track_uri")
conteo <- pistas_mas_sonadas %>% count(track_uri)
pistas_mas_sonadas <- left_join(new_df_tracks, conteo, by="track_uri")
pistas_mas_sonadas <- arrange(pistas_mas_sonadas,desc(n))
pistas_mas_sonadas_top <- distinct(pistas_mas_sonadas,track_uri,.keep_all = TRUE)
pistas_mas_sonadas_top

##pistas_mas_sonadas <- arrange(pistas_mas_sonadas,desc(n))
##pistas_mas_sonadas_top <- filter(distinct(pistas_mas_sonadas,track_uri))
  
  

ahora guardaremos en un dataframe los 1000 mas escuchados y desplegaremos los 10 mas escuchados


top_1000 <- head(pistas_mas_sonadas_top,1000)
top_10 <- head(pistas_mas_sonadas_top,10)
top_10

realizamos el grafico de audiencia del top 10


grafico <- ggplot(top_10, aes(x = track_name, y = n)) +
  geom_bar(stat = "identity") + theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
grafico

ahora veremos cual es el streaming mas escuchado, de base tendremos el top de las 10 canciones mas escuchadas y el top de los streamings dependerá del numero de holdouts y del numero de pistas que tenga

streamings_mas_pausados <- arrange(df_spotify,desc(num_holdouts))
streamings_mas_pausados

vemos cual es el mejor streaming

mejor_streaming <- df_spotify%>%
  filter(indice == top_10$dataframe_id)
mejor_streaming

lista_pistas <- new_df_tracks%>%
  filter(dataframe_id == 8)
lista_pistas

contiene una pista que esta en el top 10 , no contiene solo 5 pistas

Objetivo 3.-

Relacionar las características de cada canción con los resultados para determinar que tienen en común.

jugaremos con el top 1000 de canciones, haciendo un grafico de dispersión respecto al tiempo de duración de la canción con el top

relación_dpopularidad_duración_pista <- ggplot(top_1000, aes(x = n, y = duration_ms)) +
  geom_point()
relación_dpopularidad_duración_pista

podemos concluir que las canciones mas escuchadas están en un rango entre 2 minutos y 3 minutos, la mas escuchada está en el intervalo menor a 2 minutos y medio

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLiANCg0KYGBge3J9DQojI2luc3RhbGwucGFja2FnZXMoImRwbHlyIikNCiMjaW5zdGFsbC5wYWNrYWdlcygianNvbmxpdGUiKQ0KIyNpbnN0YWxsLnBhY2thZ2VzKCJyanNvbiIpDQojI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShjb25mbGljdGVkKQ0KbGlicmFyeShyanNvbikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGpzb25saXRlKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg0KVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLg0KDQojIHByb3llY3RvIHN0cmVhbSBleGl0b3NvIGVuIHNwb3RpZnkNCg0KIyBFbCBvYmpldGl2byBkZSBlc3RlIHByb3llY3RvIGVzIGFuYWxpemFyIG1lZGlhbnRlIGxvcyBkYXRvcyBwb3Igbml2ZWxlcyBkZSB1biBKU09OIHkgbG9zIGRhdG9zIGRlIGVudHJhZGEgZGUgdW4gQ1NWIHF1ZSBjb250aWVuZW4gbG9zIGlkIGRlIGxvcyBzdHJlYW1pbmcgeSBsYXMgcGlzdGFzIGVzY3VjaGFkYXMsIGN1YWwgZXMgZWwgZXN0cmVhbSB5IGxhIGNhbmNpw7NuIG1hcyBlc2N1Y2hhZGEgeSBvYnNlcnZhciBsYSByZWxhY2nDs24gZW50cmUgdW4gcmFuZ28gZGUgZHVyYWNpw7NuIGRlIGxhcyBtaXNtYXMgcGFyYSBkZXRlcm1pbmFyIGVudHJlIGxhcyBtZWpvcmVzIGNhbmNpb25lcyBzaSBoYXkgYWxndW5hIGluZmx1ZW5jaWEgZW4gbGEgZHVyYWNpw7NuIGRlIGxhIGNhbmNpw7NuIHBhcmEgc3Ugw6l4aXRvIHBhcmEgc2VyIHRlbmRlbmNpYS4NCg0KIyMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBDYXJnYW1vcyBlbCBwcmltZXIgZGF0YXNldA0KDQoNCiMjICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KDQojIyBwcmltZXJvIGxlZW1vcyBsYSBkYXRhIGRlc2RlIGVsIHByb3BpbyBKU09ODQpgYGB7cn0NCmRmIDwtIGZyb21KU09OKCJDOi9Vc2Vycy9FZGdhci9Eb2N1bWVudHMvR2l0SHViL0RhdGFNaW5pbmdfYW5kX01hY2hpbmVMZWFybmluZ19FZGdhclYvZGF0YS9Qcm95ZWN0bzEvY2hhbGxlbmdlX3NldC5qc29uIikNCmRmDQpgYGANCg0KIyMgUGFzYW1vcyBhIHVuIGRhdGEgZnJhbWUgbG9zIGR0b3MgbWFzIHJlbGV2YW50ZXMgcGFyYSBub3NvdHJvcyBxdWUgc2Vyw61hIGxhIFBsYXlsaXN0DQpgYGB7cn0NCmRmX3Nwb3RpZnkgPC0gZGYkcGxheWxpc3RzDQpkZl9zcG90aWZ5IDwtIGRmX3Nwb3RpZnklPiUNCiAgbXV0YXRlKGluZGljZSA9IHJvdy5uYW1lcyguKSkNCmRmX3Nwb3RpZnkNCmBgYA0KDQojIyBFeHBsb3JhbW9zIGxvcyBkYXRvcw0KYGBge3J9DQpkZl90cmFja3MgPC0gZGZfc3BvdGlmeSR0cmFja3MNCmhlYWQoZGZfdHJhY2tzKQ0KIyMgb2JzZXJ2YW1vcyBxdWUgcGFyYSBjYWRhIGxpc3RhIGRlIHBpc3RhcyBoYXkgdmFsb3JlcyBhc29jaWFkb3MgYSBjYWRhIHBpc3RhIGNvbW8gbm9tYnJlIGRlIGFydGlzdGEsIG5vbWJyZSBkZSBsYSBwaXN0YSB5IGR1cmFjaW9uDQpgYGANCg0KIyMgdmVyaWZpY2Ftb3Mgc2kgbmluZ3VuIGRhdG8gdGllbmUgZWxlbWVudG9zIE5BDQpgYGB7cn0NCg0KYW55KGlzLm5hKGRmX3RyYWNrcykpDQpgYGANCg0KIyMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBDYXJnYW1vcyBlbCBzZWd1bmRvIGRhdGFzZXQNCg0KDQojIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCg0KIyMgZWxpbWluYXJlbW9zIGNvbHVtbmFzIHF1ZSBubyBub3Mgc2lydmFuLCBwZXJvIGFudGVzIGRlYmVtb3MgYW5hbGl6YXIgZWwgZGF0YXNldCBkZSBsb3MgbnVtZXJvcyBkZSBzdHJlYW1pbmdzDQoNCmBgYHtyfQ0KZGZfZGF0YV9zcG90aWZ5IDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9FZGdhci9Eb2N1bWVudHMvR2l0SHViL0RhdGFNaW5pbmdfYW5kX01hY2hpbmVMZWFybmluZ19FZGdhclYvZGF0YS9Qcm95ZWN0bzEvc2FtcGxlX3N1Ym1pc3Npb24uY3N2IikNCg0KIyMgZGVmaW5pbW9zIGN1YWwgc2Vyw6EgbGEgcHJpbWVyYSBjb2x1bW5hDQoNCnByaW1lcmFfY29sdW1uYSA8LSBkZl9kYXRhX3Nwb3RpZnkkWDEwNDg1ODgNCiMjaGVhZChwcmltZXJhX2NvbHVtbmEsMTApDQoNCiMjIHRyYW5zZm9ybWFtb3MgbGFzIGZpbGFzIHJlc3RhbnRlcyBlbiBsaXN0YXMNCmxpc3Rhc19maWxhcyA8LSBsYXBwbHkoMTpucm93KGRmX2RhdGFfc3BvdGlmeSksZnVuY3Rpb24oaSkgYXMubGlzdCh0KGRmX2RhdGFfc3BvdGlmeVtpLC0xXSkpKQ0KDQojI2xpc3Rhc19maWxhcw0KZGZfdHJhY2tzX3VyaSA8LSBkYXRhLmZyYW1lKHRyYWNrc191cmkgPSB1bmxpc3QobGlzdGFzX2ZpbGFzKSkNCmhlYWQoZGZfdHJhY2tzX3VyaSwyKQ0KDQojIyBlc3RlIGNvZGlnbyBjb21iaW5hcmlhIGNhZGEgZWxlbWVudG8gY29uIHVuYSBsaXN0YSBkZSBlbGVtZW50b3MgcXVlIGNvbmZvcm1hbiBsYSBmaWxhDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjZGZfdHJhY2tzX3VyaSA8LSBkYXRhLmZyYW1lKHRyYWNrc191cmkgPSBsaXN0YXNfZmlsYXMpDQoNCiMjY29tYmluYW1vcyBsYXMgY29sdW1uYXMgZGUgbG9zIHBpZCBjb24gbGFzIGxpc3Rhcw0KIyNkZl9kYXRhX3Nwb3RpZnlfY29tYmluYWRvIDwtIGRhdGEuZnJhbWUocGlkID0gcHJpbWVyYV9jb2x1bW5hLHRyYWNrc191cmkgPSBsaXN0YXNfZmlsYXMpDQojI2hlYWQoZGZfZGF0YV9zcG90aWZ5X2NvbWJpbmFkbywyKQ0KIyNoZWFkKGRmX2RhdGFfc3BvdGlmeV9jb21iaW5hZG8kdHJhY2tzX3VyaSwxKQ0KIyNoZWFkKGRmX2RhdGFfc3BvdGlmeV9jb21iaW5hZG8kdHJhY2tzX3VyaSwyKQ0KIyNkZl9kYXRhX3Nwb3RpZnlfY29tYmluYWRvIDwtIGFzLmRhdGEuZnJhbWUoZG8uY2FsbChyYmluZCwgbGlzdGFzX2NvbWJpbmFkYXMpKQ0KIyNoZWFkKGRmX2RhdGFfc3BvdGlmeV9jb21iaW5hZG8pDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCiMjdGlwb19kZV9kYXRvIDwtIHNhcHBseShkZl9kYXRhX3Nwb3RpZnksIGNsYXNzKQ0KIyNkZl9kYXRhX3Nwb3RpZnlfdW5pZmljYWRhIDwtIHNwbGl0KGRmX2RhdGFfc3BvdGlmeSx0aXBvX2RlX2RhdG8pDQoNCmBgYA0KIyBleHBsb3JhbW9zIGRhdG9zIHBvciDDumx0aW1hIHZleiBhbnRlcyBkZSBpciBhIGxvIG51ZXN0cm8NCiMjIGRhdG9zIHBpZA0KYGBge3J9DQpwcmltZXJhX2NvbHVtbmEgPC0gZGZfZGF0YV9zcG90aWZ5JFgxMDQ4NTg4DQpkZl9waWQgPC0gZGF0YS5mcmFtZShwaWQgPSBwcmltZXJhX2NvbHVtbmEpDQpoZWFkKGRmX3BpZCwxMCkNCmBgYA0KDQojIyBkYXRvcyB0cmFja3NfdXJpDQpgYGB7cn0NCmRmX2RhdGFfc3BvdGlmeSA8LSByZWFkLmNzdigiQzovVXNlcnMvRWRnYXIvRG9jdW1lbnRzL0dpdEh1Yi9EYXRhTWluaW5nX2FuZF9NYWNoaW5lTGVhcm5pbmdfRWRnYXJWL2RhdGEvUHJveWVjdG8xL3NhbXBsZV9zdWJtaXNzaW9uLmNzdiIpDQoNCiMjIHRyYW5zZm9ybWFtb3MgbGFzIGZpbGFzIHJlc3RhbnRlcyBlbiBsaXN0YXMNCmxpc3Rhc19maWxhcyA8LSBsYXBwbHkoMTpucm93KGRmX2RhdGFfc3BvdGlmeSksZnVuY3Rpb24oaSkgYXMubGlzdCh0KGRmX2RhdGFfc3BvdGlmeVtpLC0xXSkpKQ0KDQojI2RhdGFmcmFtZSBkZSBsaXN0YXNfZmlsYXMNCmRmX3RyYWNrc191cmkgPC0gZGF0YS5mcmFtZSh0cmFja191cmkgPSB1bmxpc3QobGlzdGFzX2ZpbGFzKSkNCmhlYWQoZGZfdHJhY2tzX3VyaSwyKQ0KYGBgDQoNCg0KIyBleHBsb3JhbW9zIGRhdG9zIHBvciDDumx0aW1hIHZleiBhbnRlcyBkZSBpciBhIGxvIG51ZXN0cm8NCiMjIGRhdG9zIHRyYWNrX3VyaQ0KYGBge3J9DQpoZWFkKGRmX3RyYWNrc191cmksMTAwMCkNCmBgYA0KDQojIyB1bmEgdmV6IG9idGVuaWRvcyBsb3MgdmFsb3JlcyBxdWUgbmVjZXNpdGFtb3MgcHJvY2VkZW1vcyBhIGNvbnRhcmxvcw0KDQojIyBwcmltZXJvIGVtcGV6YXJlbW9zIGNvbiBsb3MgZGF0b3MgcGlkIHF1ZSBub3MgZGEgbGEgaW5mb3JtYWNpw7NuIGRlIGN1YW50b3Mgc3RyZWFtaW5ncyBodWJvIGVuIGVsIHByb2Nlc28gZGUgcmVjb2xlY2Npw7NuIGRlIGRhdG9zDQoNCmBgYHtyfQ0KZGZfbnVtZXJvX3N0cmVhbWluZ3MgPC0gZGZfcGlkJT4lDQogIHNlbGVjdChwaWQpJT4lDQogIGNvdW50KCkNCmRmX251bWVyb19zdHJlYW1pbmdzDQpwcmludCgib2pvIHF1ZSBoYXkgcXVlIGRlc2NvbnRhciAxMDAwIGRhdG9zIHF1ZSBlc3RhYmFuIHZhY2lvcyBhbCByZWFsaXphciBsYSBsaW1waWV6YSBkZSBsb3MgZGF0b3MgIikNCmBgYA0KDQoNCiMjI3JldmlzYW1vcyBzaSBoYXkgZGF0b3MgZHVwbGljYWRvcw0KYGBge3J9DQpkZl9udW1lcm9fc3RyZWFtaW5nc19kdXBsaWNhZG9zIDwtIGRmX3BpZCU+JQ0KICBmaWx0ZXIoZHVwbGljYXRlZChwaWQpKSU+JQ0KICBjb3VudCgpDQpkZl9udW1lcm9fc3RyZWFtaW5nc19kdXBsaWNhZG9zDQpgYGANCiMjIGNvbiBlc3RvIGNvbXByb2JhbW9zIHF1ZSBoYXkgMTAwMDAgZGF0b3MgZGUgc2VzacOzbiBkZSBzdHJlYW1pbmdzDQoNCiMjIGFob3JhIHZlcmlmaXF1ZW1vcyBzaSBoYXkgZGF0b3MgcmVwZXRpZG9zIGVuIGxhIGNvbHVtbmEgZGUgdHJhY2tzX3VyaSBxdWUgbm9zIG11ZXN0cmEgbGEgaW5mb3JtYWNpw7NuIGRlIGxhIHBpc3RhDQoNCmBgYHtyfQ0KZGZfbnVtZXJvX3RyYWNrc19yZXBldGlkb3MgPC0gZGZfdHJhY2tzX3VyaSU+JQ0KICBmaWx0ZXIoZHVwbGljYXRlZCh0cmFja3NfdXJpKSklPiUNCiAgY291bnQoKQ0KZGZfbnVtZXJvX3RyYWNrc19yZXBldGlkb3MNCmBgYA0KIyMgYWhvcmEgY29udGFtb3MgbG9zIGRhdG9zIHRvdGFsZXMgZGUgbGFzIHBpc3RhbA0KYGBge3J9DQpkZl9udW1lcm9fdG90YWxfdHJhY2tzIDwtIGRmX3RyYWNrc191cmklPiUNCiAgc2VsZWN0KHRyYWNrc191cmkpJT4lDQogIGNvdW50KCkNCmRmX251bWVyb190b3RhbF90cmFja3MNCmBgYA0KIyMgcmVhbGl6YW1vcyBsYSBkaWZlcmVuY2lhIHBhcmEgY29udGFiaWxpemFyIGVsIG51bWVybyByZWFsIGRlIHBpc3Rhcw0KYGBge3J9DQpudW1fcmVhbF9waXN0YXMgPC0gZGZfbnVtZXJvX3RvdGFsX3RyYWNrcyAtIGRmX251bWVyb190cmFja3NfcmVwZXRpZG9zDQpudW1fcmVhbF9waXN0YXMNCmBgYA0KDQojIyBhaG9yYSBjb250YW1vcyBsb3Mgc3RyZWFtaW5ncyBxdWUgdGllbmUgZWwgb3RybyBkYXRhc2V0DQoNCmBgYHtyfQ0KDQpudW1fc3RyZWFtaW5ncyA8LSBkZl9zcG90aWZ5JT4lDQogIHNlbGVjdChwaWQpJT4lDQogIGNvdW50KCkNCm51bV9zdHJlYW1pbmdzDQoNCmBgYA0KIyMjIyBwb3IgbG8gdmlzdG8gZWwgbsO6bWVybyBkZSBzdHJlYW1pbmdzIGNvaW5jaWRlbiBjb24gbG9zIGRhdG9zIGRlbCBkYXRhc2V0IGRlIGxhcyBwaXN0YXMNCg0KIyMgYWhvcmEgY29udGFtb3MgbG9zIGRhdG9zIGRlIGxhcyBwaXN0YXMgZGVsIGRhdGFzZXQgY2hhbGxlbmdlDQoNCiMjIHByaW1lcm8gdW5pbW9zIGxvcyBkYXRhZnJhbWVzIGVuIHVubyBzb2xvDQoNCmBgYHtyfQ0KbmV3X2RmX3RyYWNrcyA8LSBiaW5kX3Jvd3MoZGZfdHJhY2tzLCAuaWQgPSAiZGF0YWZyYW1lX2lkIikNCm5ld19kZl90cmFja3MNCmBgYA0KDQojIyBhaG9yYSBjb250YW1vcyBlbCBudW1lcm8gZGUgcGlzdGFzIHF1ZSB0aWVuZSBlc3RlIGRhdGFzZXQNCg0KYGBge3J9DQpudW1fdHJhY2tzIDwtIG5ld19kZl90cmFja3MlPiUNCiAgc2VsZWN0KHRyYWNrX3VyaSklPiUNCiAgY291bnQoKQ0KbnVtX3RyYWNrcw0KYGBgDQojIyByZXZpc2Ftb3Mgc2kgbm8gaGF5IHJlcGV0aWRvcw0KDQpgYGB7cn0NCm51bV90cmFja3MgPC0gbmV3X2RmX3RyYWNrcyU+JQ0KICBkaXN0aW5jdCh0cmFja191cmkpJT4lDQogIGNvdW50KCkNCm51bV90cmFja3MNCmBgYA0KIyMgY29tcGFyYW1vcyBjb24gZWwgbsO6bWVybyByZWFsIGRlIHBpc3Rhcw0KDQpgYGB7cn0NCmNvbXBhcmlzb24gPC0gZGF0YS5mcmFtZShudW1fdHJhY2tzX0pTT049bnVtX3JlYWxfcGlzdGFzLCBudW1fdHJhY2tzX2Nzdj0gbnVtX3RyYWNrcyApDQpjb21wYXJpc29uDQpgYGANCiMjIHBvciBsbyB0YW50byBsb3MgZGF0b3MgdmFuIGFjb3JkZSBjb24gZXN0ZSBwcm95ZWN0bw0KDQojIE9iamV0aXZvIDEuLQ0KIyNFeHRyYWVyIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGRlIGxvcyBzdHJlYW1pbmcgZGUgY2FuY2lvbmVzIHkgZGUgc3VzIHBpc3RhcyBjb21vIHN1IG5vbWJyZSwgYXJ0aXN0YSwgZWwgbsO6bWVybyBkZSBwaXN0YXMgKGVuIGVsIGNhc28gZGVsIFN0cmVhbWluZykgeSBsYSBkdXJhY2nDs24gZGUgbGEgcGlzdGEuDQoNCmBgYHtyfQ0KaGVhZChkZl9zcG90aWZ5LDEwKQ0KaGVhZChuZXdfZGZfdHJhY2tzLDEwKQ0KYGBgDQoNCiMgT2JqZXRpdm8gMi4tDQoNCiMjIEhhbGxhciBsYXMgcGlzdGFzIG1hcyBzb25hZGFzIHkgbG9zIHN0cmVhbWluZ3MgbcOhcyBlc2N1Y2hhZG9zLg0KDQpgYGB7cn0NCnBpc3Rhc19tYXNfc29uYWRhcyA8LSBsZWZ0X2pvaW4obmV3X2RmX3RyYWNrcywgZGZfdHJhY2tzX3VyaSwgYnk9InRyYWNrX3VyaSIpDQpjb250ZW8gPC0gcGlzdGFzX21hc19zb25hZGFzICU+JSBjb3VudCh0cmFja191cmkpDQpwaXN0YXNfbWFzX3NvbmFkYXMgPC0gbGVmdF9qb2luKG5ld19kZl90cmFja3MsIGNvbnRlbywgYnk9InRyYWNrX3VyaSIpDQpwaXN0YXNfbWFzX3NvbmFkYXMgPC0gYXJyYW5nZShwaXN0YXNfbWFzX3NvbmFkYXMsZGVzYyhuKSkNCnBpc3Rhc19tYXNfc29uYWRhc190b3AgPC0gZGlzdGluY3QocGlzdGFzX21hc19zb25hZGFzLHRyYWNrX3VyaSwua2VlcF9hbGwgPSBUUlVFKQ0KcGlzdGFzX21hc19zb25hZGFzX3RvcA0KYGBgDQoNCiMjIGFob3JhIGd1YXJkYXJlbW9zIGVuIHVuIGRhdGFmcmFtZSBsb3MgMTAwMCBtYXMgZXNjdWNoYWRvcyB5IGRlc3BsZWdhcmVtb3MgbG9zIDEwIG1hcyBlc2N1Y2hhZG9zDQoNCmBgYHtyfQ0KDQp0b3BfMTAwMCA8LSBoZWFkKHBpc3Rhc19tYXNfc29uYWRhc190b3AsMTAwMCkNCnRvcF8xMCA8LSBoZWFkKHBpc3Rhc19tYXNfc29uYWRhc190b3AsMTApDQp0b3BfMTANCmBgYA0KDQojIyByZWFsaXphbW9zIGVsIGdyYWZpY28gZGUgYXVkaWVuY2lhIGRlbCB0b3AgMTANCg0KYGBge3J9DQoNCmdyYWZpY28gPC0gZ2dwbG90KHRvcF8xMCwgYWVzKHggPSB0cmFja19uYW1lLCB5ID0gbikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKQ0KZ3JhZmljbw0KYGBgDQoNCiMjIGFob3JhIHZlcmVtb3MgY3VhbCBlcyBlbCBzdHJlYW1pbmcgbWFzIGVzY3VjaGFkbywgZGUgYmFzZSB0ZW5kcmVtb3MgZWwgdG9wIGRlIGxhcyAxMCBjYW5jaW9uZXMgbWFzIGVzY3VjaGFkYXMgeSBlbCB0b3AgZGUgbG9zIHN0cmVhbWluZ3MgZGVwZW5kZXLDoSBkZWwgbnVtZXJvIGRlIGhvbGRvdXRzIHkgZGVsIG51bWVybyBkZSBwaXN0YXMgcXVlIHRlbmdhDQoNCmBgYHtyfQ0Kc3RyZWFtaW5nc19tYXNfcGF1c2Fkb3MgPC0gYXJyYW5nZShkZl9zcG90aWZ5LGRlc2MobnVtX2hvbGRvdXRzKSkNCnN0cmVhbWluZ3NfbWFzX3BhdXNhZG9zDQpgYGANCiMjIHZlbW9zIGN1YWwgZXMgZWwgbWVqb3Igc3RyZWFtaW5nDQpgYGB7cn0NCm1lam9yX3N0cmVhbWluZyA8LSBkZl9zcG90aWZ5JT4lDQogIGZpbHRlcihpbmRpY2UgPT0gdG9wXzEwJGRhdGFmcmFtZV9pZCkNCm1lam9yX3N0cmVhbWluZw0KDQpsaXN0YV9waXN0YXMgPC0gbmV3X2RmX3RyYWNrcyU+JQ0KICBmaWx0ZXIoZGF0YWZyYW1lX2lkID09IDgpDQpsaXN0YV9waXN0YXMNCmBgYA0KIyMgY29udGllbmUgdW5hIHBpc3RhIHF1ZSBlc3RhIGVuIGVsIHRvcCAxMCAsIG5vIGNvbnRpZW5lIHNvbG8gNSBwaXN0YXMgDQoNCiMgT2JqZXRpdm8gMy4tDQoNCiMjIFJlbGFjaW9uYXIgbGFzIGNhcmFjdGVyw61zdGljYXMgZGUgY2FkYSBjYW5jacOzbiBjb24gbG9zIHJlc3VsdGFkb3MgcGFyYSBkZXRlcm1pbmFyIHF1ZSB0aWVuZW4gZW4gY29tw7puLg0KDQojIyMganVnYXJlbW9zIGNvbiBlbCB0b3AgMTAwMCBkZSBjYW5jaW9uZXMsIGhhY2llbmRvIHVuIGdyYWZpY28gZGUgZGlzcGVyc2nDs24gcmVzcGVjdG8gYWwgdGllbXBvIGRlIGR1cmFjacOzbiBkZSBsYSBjYW5jacOzbiBjb24gZWwgdG9wDQoNCmBgYHtyfQ0KcmVsYWNpw7NuX2Rwb3B1bGFyaWRhZF9kdXJhY2nDs25fcGlzdGEgPC0gZ2dwbG90KHRvcF8xMDAwLCBhZXMoeCA9IG4sIHkgPSBkdXJhdGlvbl9tcykpICsNCiAgZ2VvbV9wb2ludCgpDQpyZWxhY2nDs25fZHBvcHVsYXJpZGFkX2R1cmFjacOzbl9waXN0YQ0KYGBgDQojIyBwb2RlbW9zIGNvbmNsdWlyIHF1ZSBsYXMgY2FuY2lvbmVzIG1hcyBlc2N1Y2hhZGFzIGVzdMOhbiBlbiB1biByYW5nbyBlbnRyZSAyIG1pbnV0b3MgeSAzIG1pbnV0b3MsIGxhIG1hcyBlc2N1Y2hhZGEgZXN0w6EgZW4gZWwgaW50ZXJ2YWxvIG1lbm9yIGEgMiBtaW51dG9zIHkgbWVkaW8NCg0KIyMgY29tbyBjb25jbHVzacOzbiBmaW5hbCB0ZW5lbW9zIHF1ZSBwYXJhIHF1ZSB1bmEgY2FuY2nDs24gc2VhIHBvcHVsYXIgc3UgZHVyYWNpw7NuIGRlYmUgZXN0YXIgYXBlZ2FkYSBhbCByYW5nbyBkZSBlbnRyZSAyIGEgMiBtaW51dG9zIHkgbWVkaW8NCg0KDQoNCg0K